package com.zhuzhe.mq.mqtype.rabbitmq.init;

import java.util.LinkedList;
import java.util.Map;
import java.util.UUID;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Scope;

/**
 * @author whh
 * @date 2020/08/24 20:31
 */
public class RabbitmqConfig implements ApplicationContextAware {
    private static Logger log = LoggerFactory.getLogger("MQ");
    @Value("${spring.rabbitmq.address}")
    String address;
    @Value("${spring.rabbitmq.username}")
    String username;
    @Value("${spring.rabbitmq.password}")
    String password;
    @Value("${spring.rabbitmq.virtual-host:}")
    String mqRabbitVirtualHost;

    @Value("${spring.application.name}")
    String applicationName;
    @Value("${mq.dispatchCenter:false}")
    boolean dispatchCenter;
    // @Autowired
    // MQServiceConfig mqServiceConfig;

    private LinkedList<Queue> allQueueList = new LinkedList<>();
    private LinkedList<Queue> allServiceList = new LinkedList<>();
    private ApplicationContext applicationContext;
    private RabbitAdmin rabbitAdmin;
    // @Autowired
    // RabbitAdmin rabbitAdmin;

    // 创建mq连接
    @Bean(name = "connectionFactory")
    public ConnectionFactory connectionFactory() {
        CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
        connectionFactory.setUsername(username);
        connectionFactory.setPassword(password);
        if (mqRabbitVirtualHost.equals("")) {
            mqRabbitVirtualHost = "/";
        }
        connectionFactory.setVirtualHost(mqRabbitVirtualHost);
        connectionFactory.setPublisherConfirms(true);
        // 该方法配置多个host，在当前连接host down掉的时候会自动去重连后面的host
        connectionFactory.setAddresses(address);
        return connectionFactory;
    }

    @Bean
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    // 必须是prototype类型
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
        RabbitTemplate template = new RabbitTemplate(connectionFactory);
        return template;
    }

    // 创建初始化RabbitAdmin对象
    @Bean
    public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory) {
        RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory);
        // 只有设置为 true，spring 才会加载 RabbitAdmin 这个类
        rabbitAdmin.setAutoStartup(true);
        this.rabbitAdmin = rabbitAdmin;
        return rabbitAdmin;
    }

    public void bindQueueToExchange(String serverName, boolean bindAll, boolean addToAllQueueList) {
        if (allServiceList.contains(serverName)) {
            return;
        }
        String queuename = "com.zhuzhe." + serverName + "-queue";
        String exchangename = "com.zhuzhe." + serverName + "";
        String exchangeAllname = "com.zhuzhe." + serverName + "All";
        // 创建topicExchange
        TopicExchange topicExchange = new TopicExchange(exchangename, true, false);
        rabbitAdmin.declareExchange(topicExchange);
        // 创建topicExchange 所需的queue
        Queue queue = new Queue(queuename, true);
        rabbitAdmin.declareQueue(queue);
        // topicExchange与queue绑定
        Binding binding = BindingBuilder.bind(queue).to(topicExchange).with(exchangename);
        rabbitAdmin.declareBinding(binding);
        // 加入到所有监听的list列表中
        if (addToAllQueueList) {
            allQueueList.add(queue);
        }
        if (bindAll == true) {
            // 创建fanoutExchange
            FanoutExchange fanoutExchange = new FanoutExchange(exchangeAllname, true, false);
            rabbitAdmin.declareExchange(fanoutExchange);
            // 创建randomQueue 所需的queue
            Queue randomQueue = new Queue(queuename + "-" + UUID.randomUUID(), true, true, true);
            rabbitAdmin.declareQueue(randomQueue);
            // fanoutExchange与randomQueue绑定
            Binding randomBinding = BindingBuilder.bind(randomQueue).to(fanoutExchange);
            rabbitAdmin.declareBinding(randomBinding);
            if (addToAllQueueList) {
                allQueueList.add(randomQueue);
            }
        }

        log.info("绑定消息队列到交换机：" + serverName);
    }

    /**
     * 根据配置绑定队列到交换器,匹配路由规则
     *
     * @return
     */
    @Bean
    public boolean bindingExchangeMessage() {
        if (dispatchCenter == true) {
            this.bindQueueToExchange("dispatchCenter", false, true);
        } else {
            this.bindQueueToExchange("dispatchCenter", false, false);
        }
        this.bindQueueToExchange(applicationName, true, true);
        return true;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    // 创建监听器，监听队列
    @Bean
    public SimpleMessageListenerContainer mqMessageContainer(ConnectionFactory connectionFactory) throws AmqpException {
        SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
        Queue[] queues = allQueueList.toArray(new Queue[allQueueList.size()]);
        container.setQueues(queues);
        container.setExposeListenerChannel(true);
        container.setPrefetchCount(1);// 设置每个消费者获取的最大的消息数量
        // container.setConcurrentConsumers(concurrentConsumers);//消费者个数
        container.setAcknowledgeMode(AcknowledgeMode.MANUAL);// 设置确认模式为手工确认

        MessageListener messageListener = new MQMessageHandleService();
        Map<String, MQMessageHandleService> map = this.applicationContext.getBeansOfType(MQMessageHandleService.class);
        for (Map.Entry<String, MQMessageHandleService> m : map.entrySet()) {
            messageListener = m.getValue();
        }

        if (messageListener != null) {
            container.setMessageListener(messageListener);// 监听处理类
        }
        // MQMessageHandleService handleService=new MQMessageHandleService();
        // container.setMessageListener(handleService);//监听处理类
        return container;
    }
}
